#include "StandardShaders.h"
#include "Shader.h"
#include "ShaderRegistry.h"
#include "Scene.h"
#include "RayTraceSystem.h"
#include "TextureData.h"

namespace RayTracePro
{
	class BaseShadingNode : public ShadingNode
	{
	public:
		virtual ~BaseShadingNode()
		{};

		virtual void SimplifiedShade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			Shade(result, dgeom, ray, rayDiff, scene, depth);
		}
	};

	bool EvalLightRadiance(Vec3 & rs, Vec3 & l, RayTraceSystem * system, Scene * scene, Light * light, const Vec3 & p)
	{
		float decay = 1.0f;
		float dist;
		rs.SetZero();
		if (light->IsDirectional)
		{
			l = light->Direction;
			l.x = -l.x; l.y = -l.y; l.z = -l.z;
		}
		else
		{
			Vec3::Subtract(l, light->Position, p);
			dist = l.Length();
			Vec3::Scale(l, l, 1.0f/dist);
			if (light->Cutoff)
			{
				float dot = -Vec3::Dot(l, light->Direction);
				if (dot < light->FullCutoffCos)
				{
					return false;
				}
				else if (dot < light->StartCutoffCos)
				{
					float ang = acos(dot);
					if (dot > light->HalfCutoffAngle)
					{
						float t = (ang - light->StartCutoffAngle) * light->RcpHalfMinusStart;
						decay = 1.0f - t*0.5f;
					}
					else
					{
						float t = (ang - light->HalfCutoffAngle) * light->RcpFullMinusHalf;
						decay = (1.0f - t) * 0.5f;
					}
				}
			}
			if (light->Decay == Light::Linear)
			{
				decay *= 1.0f / Min(1.0f, dist * light->DecayFactor);
			}
			else if (light->Decay == Light::Quadric)
			{
				decay *= 1.0f / Max(1.0f, dist * dist * light->DecayFactor);
			}
			/*if (decay < 0.001f)
			{
				return false;
			}*/
		}
		if (light->CastShadow)
		{
			float t;
			Ray shadowRay;
			if (!light->IsDirectional)
			{
				shadowRay.Origin = p;
				shadowRay.Direction = l;
				shadowRay.ReciprocalDirection.x = 1.0f / shadowRay.Direction.x;
				shadowRay.ReciprocalDirection.y = 1.0f / shadowRay.Direction.y;
				shadowRay.ReciprocalDirection.z = 1.0f / shadowRay.Direction.z;
				shadowRay.tMin = system->ShadowBias;
				shadowRay.tMax = dist;
			}
			else
			{
				shadowRay.Origin = p;
				shadowRay.Direction = Vec3(-light->Direction.x, -light->Direction.y, -light->Direction.z);
				shadowRay.ReciprocalDirection.x = 1.0f / shadowRay.Direction.x;
				shadowRay.ReciprocalDirection.y = 1.0f / shadowRay.Direction.y;
				shadowRay.ReciprocalDirection.z = 1.0f / shadowRay.Direction.z;
				shadowRay.tMin = system->ShadowBias;
				shadowRay.tMax = FLT_MAX;
			}
			if (scene->TraceRayPredCore(t, shadowRay))
				return false;
		}
		Vec3 tmp;
		Vec3::Scale(tmp, light->Intensity, decay);
		rs = tmp;
		return true;
	}

	class TextureShadingNode : public BaseShadingNode
	{
	private:
		Vec2 uvTranslate, uvScale;
		TextureData * texture;
		RayTraceSystem * system;
		ShadingNodeParameter multiplier;
	public:
		TextureShadingNode()
		{
			texture = 0;
			uvTranslate.SetZero();
			uvScale.x = 1.0f;
			uvScale.y = 1.0f;
			multiplier.Set(Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		}
		virtual void Init(RayTraceSystem * system)
		{
			this->system = system;
		}
		virtual char* GetCategory()
		{
			return "Texture";
		}
		virtual ShadingNodeReturnType GetReturnType()
		{
			return ShadingNode::Float4;
		}
		virtual int GetParameterCount()
		{
			return 6;
		}
		virtual char* GetParameterName(int i)
		{
			switch (i)
			{
			case 0:
				return "U Translation";
			case 1:
				return "V Translation";
			case 2:
				return "U Scale";
			case 3:
				return "V Scale";
			case 4:
				return "Multiplier";
			default:
				return "File Name";
			}
		}
		virtual void GetParameterType(ParameterType* type, int i)
		{
			type->EnumValueCount = 0;
			switch(i)
			{
			case 0:
			case 1:
			case 2:
			case 3:
				type->BaseType = ParameterType::ParameterTypeFloat;
			case 4:
				type->BaseType = ParameterType::ParameterTypeFloat4;
			default:
				type->BaseType = ParameterType::ParameterTypeString;
			}
		}
		virtual bool SetParameter(const wchar_t * param, const GxLibMath::Vec4 & value)
		{
			if (_wcsicmp(param, L"U Translation") == 0)
			{
				uvTranslate.x = value.x;
				return true;
			}
			else if (_wcsicmp(param, L"V Translation") == 0)
			{
				uvTranslate.y = value.x;
				return true;
			}
			else if (_wcsicmp(param, L"U Scale") == 0)
			{
				uvScale.x = value.x;
				return true;
			}
			else if (_wcsicmp(param, L"V Scale") == 0)
			{
				uvScale.y = value.x;
				return true;
			}
			else if (_wcsicmp(param, L"Multiplier") == 0)
			{
				multiplier.Set(value);
			}
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, const wchar_t * value)
		{
			if (_wcsicmp(param, L"File Name") == 0)
			{
				if (texture)
					system->FreeTexture(texture);
				texture = system->LoadTexture(value);
				return true;
			}
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, ShadingNode * shader)
		{
			multiplier.Set(shader);
			return true;
		}
		virtual bool GetParameter(const wchar_t * param, ShadingNodeParameter & rs)
		{
			if (_wcsicmp(param, L"U Translation") == 0)
			{
				rs.Set(Vec4(uvTranslate.x));
				return true;
			}
			else if (_wcsicmp(param, L"V Translation") == 0)
			{
				rs.Set(Vec4(uvTranslate.y));
				return true;
			}
			else if (_wcsicmp(param, L"U Scale") == 0)
			{
				rs.Set(Vec4(uvScale.x));
				return true;
			}
			else if (_wcsicmp(param, L"V Scale") == 0)
			{
				rs.Set(Vec4(uvScale.y));
				return true;
			}
			else if (_wcsicmp(param, L"Multiplier") == 0)
			{
				rs = multiplier;
				return true;
			}
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, const wchar_t ** rs)
		{
			if (_wcsicmp(param, L"File Name") == 0)
			{
				if (texture)
					*rs = texture->FileName.GetStrPtr();
				else
					*rs = L"";
				return true;
			}
			return true;
		}
		
		virtual void SimplifiedShade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			Color pix = texture->Levels[texture->Levels.Count()-1].Pixels[0];
			result->x = pix.R/255.0f;
			result->y = pix.G/255.0f;
			result->z = pix.B/255.0f;
		}
		
		virtual void Shade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			if (!texture || texture->Width == 0 || texture->Height == 0)
			{
				result->x = result->y = result->z = 1.0f;
				result->w = 1.0f;
				return;
			}
			Vec2 uv = dgeom->UV;
			uv.x -= floor(uv.x);
			uv.y -= floor(uv.y);
			if (system->TextureFilter == RayTraceSystem::TextureFilterLinear)
			{
				SampleTextureLevel(result, texture, 0, uv);
			}
			else
			{
				if (!dgeom->HasDnDx())
					dgeom->Geometry->ComputeDndx(*dgeom, rayDiff->dPdx, rayDiff->dPdy);
				if (system->TextureFilter == RayTraceSystem::TextureFilterTrilinear)
					TrilinearSampling(result, texture, max(abs(dgeom->dUdx), abs(dgeom->dUdy)), max(abs(dgeom->dVdx), abs(dgeom->dVdy)), uv);
				else
					AnisotropicSampling(result, texture, system->TextureFilterAnisotropicRate, dgeom->dUdx, dgeom->dVdx, dgeom->dUdy, dgeom->dVdy, uv);
			}
			Vec4 m;
			multiplier.Get(&m, dgeom, ray, rayDiff, scene, depth);
			result->x *= m.x;
			result->y *= m.y;
			result->z *= m.z;
		}
		virtual ~TextureShadingNode()
		{
			if (texture)
				system->FreeTexture(texture);
		}
	};

	class LambertShadingNode : public BaseShadingNode
	{
	private:
		ShadingNodeParameter diffuse;
		RayTraceSystem * system;
	public:
		virtual void Init(RayTraceSystem * system)
		{
			this->system = system;
		}
		virtual char* GetCategory()
		{
			return "Material";
		}
		virtual ShadingNodeReturnType GetReturnType()
		{
			return ShadingNode::Float4;
		}
		virtual int GetParameterCount()
		{
			return 1;
		}
		virtual char* GetParameterName(int i)
		{
			return "Diffuse";
		}
		virtual void GetParameterType(ParameterType * rs, int i)
		{
			rs->EnumValueCount = 0;
			rs->BaseType = ParameterType::ParameterTypeFloat4;
		}
		virtual bool SetParameter(const wchar_t * param, const wchar_t * value)
		{
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, const GxLibMath::Vec4 & value)
		{
			diffuse.Set(value);
			return true;
		}
		virtual bool SetParameter(const wchar_t * param, ShadingNode * shader)
		{
			diffuse.Set(shader);
			return true;
		}
		virtual bool GetParameter(const wchar_t * param, const wchar_t ** value)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, ShadingNodeParameter & rs)
		{
			rs = diffuse;
			return true;
		}
		virtual void Shade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			Vec4 & rs = *result;
			rs = system->AmbientLight;
			rs.w = 1.0f;
			Vec4 diffuseVal;
			diffuse.Get(&diffuseVal, dgeom, ray, rayDiff, scene, depth);
			Light ** lights;
			int lightCount;
			scene->GetLights(dgeom->Position, lights, lightCount);
			for (int i = 0; i < lightCount; i++)
			{
				Vec3 rad, l;
				Vec3 pos, tmp;
				Vec3::Scale(tmp, dgeom->Normal, system->ShadowBias);
				Vec3::Add(pos, dgeom->Position, tmp);
				if (EvalLightRadiance(rad, l, system, scene, lights[i], pos))
				{
					float lambert = Max(0.0f, Vec3::Dot(l, dgeom->Normal));
					Vec3::Scale(rad, rad, lambert);
				}
				
				rs.x += rad.x; rs.y += rad.y; rs.z += rad.z;
			}
			rs.x *= diffuseVal.x; rs.y *= diffuseVal.y; rs.z *= diffuseVal.z;
		}
		virtual ~LambertShadingNode()
		{
			
		}
	};

	__forceinline void ShadeReflect(Vec4 * result, Vec3 & normal, float dot, const Ray * ray, const RayDifferential * rayDiff, 
		DifferentialGeometry * dgeom, Scene * scene, int depth, RayTraceSystem * system)
	{
		Ray reflectRay;
		Vec3 tmpVec;
		Vec3::Scale(tmpVec, normal, -2.0f * dot);
		reflectRay.Direction = ray->Direction;
		reflectRay.Direction += tmpVec;
		reflectRay.Origin = ray->Origin;
		Vec3::Scale(tmpVec, reflectRay.Direction, system->ShadowBias);
		reflectRay.Origin += tmpVec;
		reflectRay.tMax = FLT_MAX;
		reflectRay.tMin = Epsilon;
		reflectRay.ReciprocalDirection.x = 1.0f / reflectRay.Direction.x;
		reflectRay.ReciprocalDirection.y = 1.0f / reflectRay.Direction.y;
		reflectRay.ReciprocalDirection.z = 1.0f / reflectRay.Direction.z;
		DifferentialGeometry inter;
		inter.Init();
		RayDifferential diffIn = *rayDiff;
		float t;
		// Compute reflect ray differential
		if (!dgeom->HasDnDx())
			dgeom->Geometry->ComputeDndx(*dgeom, rayDiff->dPdx, rayDiff->dPdy);
		float dDNdx = Vec3::Dot(rayDiff->dDdx, normal) + Vec3::Dot(ray->Direction, dgeom->dNdx);
		float dDNdy = Vec3::Dot(rayDiff->dDdy, normal) + Vec3::Dot(ray->Direction, dgeom->dNdy);
		Vec3 tmpVec2;
		Vec3::Scale(tmpVec, dgeom->dNdx, dot);
		Vec3::Scale(tmpVec2, normal, dDNdx);
		tmpVec += tmpVec2;
		Vec3::Scale(tmpVec, tmpVec, -2.0f);
		diffIn.dDdx += tmpVec;

		Vec3::Scale(tmpVec, dgeom->dNdy, dot);
		Vec3::Scale(tmpVec2, normal, dDNdy);
		tmpVec += tmpVec2;
		Vec3::Scale(tmpVec, tmpVec, -2.0f);
		diffIn.dDdy += tmpVec;
		if (scene->TraceRayCore(inter, reflectRay, diffIn, t))
		{
			// computer transferred ray differential
			TransferRayDifferential(diffIn, reflectRay, inter.Normal, t);
			scene->Shade(*result, inter, reflectRay, diffIn, t, depth+1);
		}
		else
		{
			if (scene->Environment)
				scene->Environment->Shade(*result, reflectRay.Direction, diffIn.dDdx, diffIn.dDdy);
		}
	}

	__forceinline void ShadeRefract(Vec4 * result, float eta, float eta2, float dotNI, const Ray * ray, 
		Vec3 & normal, DifferentialGeometry * dgeom, int depth, Scene * scene,
		const RayDifferential * rayDiff, RayTraceSystem * system)
	{
		Ray refractRay;
		float k = 1.0f - eta2 * (1.0f - dotNI * dotNI);
		if (k < 0.0f)
			k = 0.0f;
		else
			k = sqrt(k);
		Vec3::Scale(refractRay.Direction, ray->Direction, eta);
		Vec3 tmpVec;
		Vec3::Scale(tmpVec, normal, -(eta*dotNI + k));
		refractRay.Direction += tmpVec;
		Vec3::Normalize(refractRay.Direction, refractRay.Direction);
		refractRay.Origin = dgeom->Position;
		Vec3::Scale(tmpVec, refractRay.Direction, system->ShadowBias);
		refractRay.Origin += tmpVec;
		refractRay.tMax = FLT_MAX;
		refractRay.tMin = Epsilon;
		refractRay.ReciprocalDirection.x = 1.0f / refractRay.Direction.x;
		refractRay.ReciprocalDirection.y = 1.0f / refractRay.Direction.y;
		refractRay.ReciprocalDirection.z = 1.0f / refractRay.Direction.z;
			
		Vec3 tmpVec2;
		DifferentialGeometry inter;
		inter.Init();
		RayDifferential diffIn = *rayDiff;
		float t;
		// Compute reflect ray differential
		if (!dgeom->HasDnDx())
			dgeom->Geometry->ComputeDndx(*dgeom, rayDiff->dPdx, rayDiff->dPdy);
		float dDNdx = Vec3::Dot(rayDiff->dDdx, normal) + Vec3::Dot(ray->Direction, dgeom->dNdx);
		float dDNdy = Vec3::Dot(rayDiff->dDdy, normal) + Vec3::Dot(ray->Direction, dgeom->dNdy);
		float dpdotn = -k;
		float mu = eta*dotNI - dpdotn;
		float dmu = (eta - eta2*dotNI/dpdotn);
		float dmudx = dmu * dDNdx;
		float dmudy = dmu * dDNdy;
		Vec3::Scale(diffIn.dDdx, rayDiff->dDdx, eta);
		Vec3::Scale(diffIn.dDdy, rayDiff->dDdy, eta);
		Vec3::Scale(tmpVec, dgeom->dNdx, eta);
		Vec3::Scale(tmpVec2, normal, dmudx);
		diffIn.dDdx -= tmpVec;
		diffIn.dDdx -= tmpVec2;
		Vec3::Scale(tmpVec, dgeom->dNdy, eta);
		Vec3::Scale(tmpVec2, normal, dmudy);
		diffIn.dDdy -= tmpVec;
		diffIn.dDdy -= tmpVec2;
		if (scene->TraceRayCore(inter, refractRay, diffIn, t))
		{
			// compute transferred ray differential
			TransferRayDifferential(diffIn, refractRay, inter.Normal, t);
			scene->Shade(*result, inter, refractRay, diffIn, t, depth+1);
		}
		else
		{
			if (scene->Environment)
				scene->Environment->Shade(*result, refractRay.Direction, diffIn.dDdx, diffIn.dDdy);
		}
	}

	class ReflectShadingNode : public BaseShadingNode
	{
	private:
		RayTraceSystem * system;
	public:
		virtual void Init(RayTraceSystem * system)
		{
			this->system = system;
		}
		virtual char* GetCategory()
		{
			return "RayTracing";
		}
		virtual ShadingNodeReturnType GetReturnType()
		{
			return ShadingNode::Float4;
		}
		virtual int GetParameterCount()
		{
			return 0;
		}
		virtual char* GetParameterName(int i)
		{
			return "null";
		}
		virtual void GetParameterType(ParameterType * rs, int i)
		{
			rs->EnumValueCount = 0;
			rs->BaseType = ParameterType::ParameterTypeFloat4;
		}
		virtual bool SetParameter(const wchar_t * param, const wchar_t * value)
		{
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, const GxLibMath::Vec4 & value)
		{
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, ShadingNode * shader)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, const wchar_t ** value)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, ShadingNodeParameter & rs)
		{
			return false;
		}
		virtual void Shade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			if (depth < system->TraceDepth)
			{
				
				// R = I - 2.0 * dot(N, I) * N
				float dot = Vec3::Dot(dgeom->Normal, ray->Direction);
				Vec3 normal = dgeom->Normal;
				if (dot > 0.0f)
				{
					dot = -dot;
					normal.x = -normal.x;
					normal.y = -normal.y;
					normal.z = -normal.z;
				}
				ShadeReflect(result, normal, dot, ray, rayDiff, dgeom, scene, depth, system);
			}
			else
			{
				result->SetZero();
				result->w = 1.0f;
			}
		}
	};

	class RefractShadingNode : public BaseShadingNode
	{
	private:
		RayTraceSystem * system;
		float refractionIndex, invRefractionIndex;
	public:
		virtual void Init(RayTraceSystem * system)
		{
			this->system = system;
			refractionIndex = 1.5f;
			invRefractionIndex = 1.0f / refractionIndex;
		}
		virtual char* GetCategory()
		{
			return "RayTracing";
		}
		virtual ShadingNodeReturnType GetReturnType()
		{
			return ShadingNode::Float4;
		}
		virtual int GetParameterCount()
		{
			return 1;
		}
		virtual char* GetParameterName(int i)
		{
			return "RefractionIndex";
		}
		virtual void GetParameterType(ParameterType * rs, int i)
		{
			rs->EnumValueCount = 0;
			rs->BaseType = ParameterType::ParameterTypeFloat;
		}
		virtual bool SetParameter(const wchar_t * param, const wchar_t * value)
		{
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, const GxLibMath::Vec4 & value)
		{
			refractionIndex = value.x;
			invRefractionIndex = 1.0f / refractionIndex;
			return true;
		}
		virtual bool SetParameter(const wchar_t * param, ShadingNode * shader)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, const wchar_t ** value)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, ShadingNodeParameter & rs)
		{
			rs.Set(Vec4(refractionIndex));
			return true;
		}
		virtual void Shade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			/*
			    k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I));
				if (k < 0.0)
					R = genType(0.0);       // or genDType(0.0)
				else
					R = eta * I - (eta * dot(N, I) + sqrt(k)) * N;
			*/
			if (depth < system->TraceDepth)
			{
				Vec3 normal = dgeom->Normal;
				float eta;
				float dotNI = Vec3::Dot(dgeom->Normal, ray->Direction);
				if (dotNI > 0.0f)
				{
					eta = refractionIndex;
					dotNI = -dotNI;
					normal.x = -normal.x;
					normal.y = -normal.y;
					normal.z = -normal.z;
				}
				else
				{
					eta = invRefractionIndex;
					
				}
				float eta2 = eta * eta;
				
				ShadeRefract(result, eta, eta2, dotNI, ray, normal, dgeom, depth, scene, rayDiff, system);
			}
			else
			{
				result->SetZero();
				result->w = 1.0f;
			}
		}
	};

	class FresnelShadingNode : public BaseShadingNode
	{
	private:
		RayTraceSystem * system;
		float refractionIndex, invRefractionIndex;
	public:
		virtual void Init(RayTraceSystem * system)
		{
			this->system = system;
			refractionIndex = 1.5f;
			invRefractionIndex = 1.0f / refractionIndex;
		}
		virtual char* GetCategory()
		{
			return "RayTracing";
		}
		virtual ShadingNodeReturnType GetReturnType()
		{
			return ShadingNode::Float4;
		}
		virtual int GetParameterCount()
		{
			return 1;
		}
		virtual char* GetParameterName(int i)
		{
			return "RefractionIndex";
		}
		virtual void GetParameterType(ParameterType * rs, int i)
		{
			rs->EnumValueCount = 0;
			rs->BaseType = ParameterType::ParameterTypeFloat;
		}
		virtual bool SetParameter(const wchar_t * param, const wchar_t * value)
		{
			return false;
		}
		virtual bool SetParameter(const wchar_t * param, const GxLibMath::Vec4 & value)
		{
			refractionIndex = value.x;
			invRefractionIndex = 1.0f / refractionIndex;
			return true;
		}
		virtual bool SetParameter(const wchar_t * param, ShadingNode * shader)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, const wchar_t ** value)
		{
			return false;
		}
		virtual bool GetParameter(const wchar_t * param, ShadingNodeParameter & rs)
		{
			rs.Set(Vec4(refractionIndex));
			return true;
		}
		virtual void Shade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth)
		{
			/*
			    k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I));
				if (k < 0.0)
					R = genType(0.0);       // or genDType(0.0)
				else
					R = eta * I - (eta * dot(N, I) + sqrt(k)) * N;
			*/
			if (depth < system->TraceDepth)
			{
				Vec3 normal = dgeom->Normal;
				float eta, n1, n2;
				float dotNI = Vec3::Dot(dgeom->Normal, ray->Direction);
				if (dotNI > 0.0f)
				{
					n1 = refractionIndex;
					n2 = 1.0f;
					eta = refractionIndex;
					dotNI = -dotNI;
					normal.x = -normal.x;
					normal.y = -normal.y;
					normal.z = -normal.z;
				}
				else
				{
					n1 = 1.0f;
					n2 = refractionIndex;
					eta = invRefractionIndex;
				}
				float eta2 = eta * eta;
				float cosThetai = -dotNI;
				float sin2Thetai = 1.0f - cosThetai * cosThetai;
				float n1cos = n1 * cosThetai;
				float n2sqrt = n2 * sqrt(1.0f - eta2 * sin2Thetai);
				float fresnel = (n1cos - n2sqrt) / (n1cos + n2sqrt);
				fresnel *= fresnel;
				float invFresnel = 1.0f - fresnel;
				Vec4 refractColor, reflectColor;
				refractColor.SetZero();
				reflectColor.SetZero();
				if (invFresnel > 0.001f)
				{
					ShadeRefract(&refractColor, eta, eta2, dotNI, ray, normal, dgeom, depth, scene, rayDiff, system);
					Vec4::Scale(refractColor, refractColor, invFresnel);
				}
				if (fresnel > 0.001f)
				{
					ShadeReflect(&reflectColor, normal, dotNI, ray, rayDiff, dgeom, scene, depth, system);
					Vec4::Scale(reflectColor, reflectColor, fresnel);
				}
				Vec4::Add(*result, reflectColor, refractColor);
			}
			else
			{
				if (scene->Environment)
					scene->Environment->Shade(*result, ray->Direction, rayDiff->dDdx, rayDiff->dDdy);
				else
					result->SetZero();
				result->w = 1.0f;
			}
		}
	};

	ShadingNode * CreateLambertShadingNode()
	{
		return new LambertShadingNode();
	}

	ShadingNode * CreateTextureShadingNode()
	{
		return new TextureShadingNode();
	}

	ShadingNode * CreateReflectShadingNode()
	{
		return new ReflectShadingNode();
	}

	ShadingNode * CreateRefractShadingNode()
	{
		return new RefractShadingNode();
	}

	ShadingNode * CreateFresnelShadingNode()
	{
		return new FresnelShadingNode();
	}


	void RegisterStandardShaders(ShaderRegistry * registry)
	{
		registry->RegisterShader(L"Lambert", CreateLambertShadingNode);
		registry->RegisterShader(L"Texture", CreateTextureShadingNode);
		registry->RegisterShader(L"Reflect", CreateReflectShadingNode);
		registry->RegisterShader(L"Refract", CreateRefractShadingNode);
		registry->RegisterShader(L"Fresnel", CreateFresnelShadingNode);
	}
}